約 3,613,824 件
https://w.atwiki.jp/vsync/pages/9.html
■ジオメトリ作成 形状、位置、姿勢など静的な情報、実際の衝突検出の対象の形状はここ。 dGeomID dCreateSphere ( dSpaceID space, dReal radius ); 球体 dGeomID dCreateBox ( dSpaceID space, dReal lx, dReal ly, dReal lz ); 直方体 dGeomID dCreateCapsule ( dSpaceID space, dReal radius, dReal length ); カプセル dGeomID dCreateCylinder ( dSpaceID space, dReal radius, dReal length ); 円柱 dGeomID dCreatePlane ( dSpaceID space, dReal a, dReal b, dReal c, dReal d ); 平面 dGeomID dCreateConvex ( 凸形状 dSpaceID space, dReal *_planes, unsigned int _planecount, dReal *_points, unsigned int _pointcount, unsigned int *_polygons ); ■ボディに紐ずける void dGeomSetBody ( dGeomID geom, dBodyID body ); 。
https://w.atwiki.jp/bambooflow/pages/243.html
アナログ上皿はかり(実験) アナログ上皿はかり(実験)設計仕様 ソースコード メモ書き まとめ 実験的に、秤を作ってみました。 スライダーによるスプリング・ダンパーの応用になります。 ギアの接触点が微妙なんですがとりあえず面白そうだったので作成してみました。 ギアのところは、正攻法とは言えないので今回は実験ものとしました。 メモ書きです。 上に乗っている球体は5Kgのものです。 量るとメーターがおよそ1/4回転します。 設計仕様 質量20Kgのものを乗せるとおおよそ針が1回転するように設計しています。 上皿に20kg乗せるとが1m下がるようにばね定数を設定 フックの法則より、 F = -k*x このとき、Fは力[N]、kはばね定数[N/m]、xはバネの伸びた長さ[m] 重力を-9.8[m/s^2]としたとすると、20kg乗せたとき1m伸びるときのばね定数は、 F = -k*x 、 -9.8*20 = -k*1 より k = 196 [N/m] となります。 次にギアの半径を決めます。 上皿が1m下がったときに針が1回転 針の回転について、上皿が1m下がったときに針が1回転するように考えたとき、ギアの円周は1mとすれば良いことになります。 よって、 2π*r = 1 r = 1 / 2π = 約 0.159 [m] ソースコード ↓ベタ書きでかなりみっともないですが。。。 #ifdef WIN32 #include windows.h #endif #include ode/ode.h #include drawstuff/drawstuff.h #include vector #ifdef dDOUBLE #define dsDrawBox dsDrawBoxD #define dsDrawSphere dsDrawSphereD #define dsDrawCylinder dsDrawCylinderD #define dsDrawCapsule dsDrawCapsuleD #define dsDrawConvex dsDrawConvexD #define dsDrawLine dsDrawLineD #define dsDrawTriangle dsDrawTriangleD #endif struct ST_Object { dGeomID geom; dBodyID body; float color[4]; ST_Object() geom(NULL), body(NULL) { color[0] = 1.0; color[1] = 1.0; color[2] = 1.0; color[3] = 1.0; } void setColor( float r, float g, float b, float a=1.0 ) { color[0] = r; color[1] = g; color[2] = b; color[3] = a; } }; ////////////////////////////////////////////////////////////////////// static dWorldID world; static dSpaceID space; static dJointGroupID contactgroup; static dJointGroupID gearjointgroup; const dReal step_size = 0.01; ST_Object* main_box = NULL; // 本体 ST_Object* dialplate = NULL; // 文字盤 ST_Object* pole = NULL; // ポール ST_Object* bowl = NULL; // 皿 ST_Object* gear0 = NULL; // ギア0 ST_Object* needle = NULL; // 目盛針 ST_Object* ball = NULL; // 錘 std vector ST_Object* drawObjList; dJointID dummy_contact0; // ポールとギア0の接点位置(ダミー) dJointID hinge; // 蝶番 ////////////////////////////////////////////////////////////////////// void drawObj( const ST_Object *obj ) { if (!obj- geom) return; const dReal *pos = dGeomGetPosition( obj- geom ); const dReal *R = dGeomGetRotation (obj- geom); dsSetColorAlpha( obj- color[0], obj- color[1], obj- color[2], obj- color[3] ); int type = dGeomGetClass (obj- geom); if (type == dBoxClass) { dVector3 sides; dGeomBoxGetLengths (obj- geom,sides); dsDrawBox (pos,R,sides); } else if (type == dSphereClass) { dsDrawSphere (pos,R,dGeomSphereGetRadius(obj- geom)); } else if (type == dCapsuleClass) { dReal radius,length; dGeomCapsuleGetParams (obj- geom, radius, length); dsDrawCapsule (pos,R,length,radius); } else if (type == dCylinderClass) { dReal radius,length; dGeomCylinderGetParams (obj- geom, radius, length); dsDrawCylinder (pos,R,length,radius); } else if (type == dRayClass) { dVector3 Origin, Direction; dGeomRayGet(obj- geom, Origin, Direction); dReal Length = dGeomRayGetLength(obj- geom); dVector3 End; End[0] = Origin[0] + (Direction[0] * Length); End[1] = Origin[1] + (Direction[1] * Length); End[2] = Origin[2] + (Direction[2] * Length); End[3] = Origin[3] + (Direction[3] * Length); dsDrawLine(Origin, End); } //else if (type == dTriMeshClass) { // int triCount = dGeomTriMeshGetTriangleCount( obj- geom ); // dReal pos_[3] = {0.0, 0.0, 0.0}; // for (int ii = 0; ii triCount; ii++) { // dVector3 v0, v1, v2; // dGeomTriMeshGetTriangle( obj- geom, ii, v0, v1, v2 ); // dsDrawTriangle( pos_, R, v0, v1, v2, 1 ); // } //} } // 衝突検出用関数 static void nearCallback( void *data, dGeomID o1, dGeomID o2 ) { dBodyID b1 = dGeomGetBody( o1 ); // 物体1 dBodyID b2 = dGeomGetBody( o2 ); // 物体2 if ( b1 b2 dAreConnectedExcluding( b1, b2, dJointTypeContact ) ) return; // 衝突対象でない物体の衝突ははずす unsigned long cat1 = dGeomGetCategoryBits(o1); unsigned long cat2 = dGeomGetCategoryBits(o2); unsigned long col1 = dGeomGetCollideBits(o1); unsigned long col2 = dGeomGetCollideBits(o2); if ((cat1 col2) || (cat2 col1)) { const int MAX_CONTACTS = 4; // 最大の衝突検出可能数 dContact contact[MAX_CONTACTS]; for ( int i=0; i MAX_CONTACTS; i++ ) { // 物体同士の接触時のパラメータ設定 contact[i].surface.mode = dContactBounce | dContactSoftERP | dContactSoftCFM | dContactMu2 | dContactSlip1 | dContactSlip2; contact[i].surface.mu = 300.0; // 摩擦係数 contact[i].surface.mu2 = 300.0; // 摩擦係数2 contact[i].surface.bounce = 0.5; // 反発係数 contact[i].surface.soft_erp = 0.3; // ERP設定 contact[i].surface.soft_cfm = 0.0001; // CFM設定 contact[i].surface.slip1 = 0.01; contact[i].surface.slip2 = 0.01; } // 衝突検出 int numc = dCollide( o1, o2, MAX_CONTACTS, contact[0].geom, sizeof( dContact ) ); if ( numc 0 ) { for ( int i=0; i numc; i++ ) { dJointID c = dJointCreateContact( world, contactgroup, contact+i ); dJointAttach( c, b1, b2 ); } } } } // start simulation - set viewpoint static void start() { static float xyz[3] = { 0.f, -5.0f, 2.f }; static float hpr[3] = { 90.f, -15.f, 0.f }; dsSetViewpoint( xyz, hpr ); } void createHakari( void ) { dReal mx = 0.0; dReal my = 0.0; dReal mz = 0.0; dReal comm_width = 0.2; dReal gear0_radius = 0.159; // 本体 { main_box = new ST_Object; dReal pos[3] = { mx+0.0, my+0.0, mz+0.5 }; dReal size[3] = { 1.0, 1.0, 1.0 }; // body setting main_box- body = dBodyCreate( world ); dBodySetPosition( main_box- body, pos[0], pos[1], pos[2] ); // geom setting main_box- geom = dCreateBox( space, size[0], size[1], size[2] ); dGeomSetBody( main_box- geom, main_box- body ); // mass setting dMass mass; dMassSetBoxTotal( mass, 1.0, size[0], size[1], size[2] ); // 1kg dBodySetMass( main_box- body, mass ); // rotation dMatrix3 R; dRFromAxisAndAngle( R, 1.0, 0.0, 0.0, 0.0*M_PI/180 ); dBodySetRotation( main_box- body, R ); main_box- setColor( 0.0, 1.0, 1.0, 0.5 ); } // 文字盤 { dialplate = new ST_Object; dReal radius = 0.5; dReal width = 0.1; dReal pos[3] = { mx+0.0, my-0.5-(width*0.5), mz+0.5 }; // body setting dialplate- body = dBodyCreate( world ); dBodySetPosition( dialplate- body, pos[0], pos[1], pos[2] ); // rotation dMatrix3 R; dRFromAxisAndAngle( R, 1.0, 0.0, 0.0, 90.0*M_PI/180 ); dBodySetRotation( dialplate- body, R ); // mass setting dMass mass; dMassSetCylinderTotal( mass, 0.3, 1, radius, width ); // 0.3kg dMassRotate( mass, R ); dBodySetMass( main_box- body, mass ); // geom setting dialplate- geom = dCreateCylinder( space, radius, width ); dGeomSetBody( dialplate- geom, dialplate- body ); dialplate- setColor( 1.0, 1.0, 1.0, 0.5 ); // 固定 dJointID fixed = dJointCreateFixed( world, 0 ); dJointAttach( fixed, dialplate- body, main_box- body ); dJointSetFixed( fixed ); } // ポール { pole = new ST_Object; dReal pole_width = 0.1; dReal pos[3] = { mx+(pole_width*0.5)+gear0_radius, my+0.0, mz+1.0 }; dReal size[3] = { pole_width, comm_width, 1.0 }; // body setting pole- body = dBodyCreate( world ); dBodySetPosition( pole- body, pos[0], pos[1], pos[2] ); // mass setting dMass mass; dMassSetBoxTotal( mass, 0.001, size[0], size[1], size[2] ); // 1g dBodySetMass( pole- body, mass ); // geom setting pole- geom = dCreateBox( space, size[0], size[1], size[2] ); dGeomSetBody( pole- geom, pole- body ); dGeomSetCategoryBits( pole- geom, 0 ); dGeomSetCollideBits( pole- geom, 0 ); pole- setColor( 0.5, 0.5, 0.5, 1.0 ); // 軸生成 dJointID slider = dJointCreateSlider( world, 0 ); dJointAttach( slider, pole- body, main_box- body ); dJointSetSliderAxis( slider, 0.0, 0.0, 1.0 ); ////////////////////////////////////////////////////////// // バネ・ダンパー設定 dReal h = step_size; dReal kp = 196; // ばね定数 (spring constant) dReal kd = 50.0; // 減衰定数 (damping constant) dReal erp = h*kp / (h*kp + kd ); dReal cfm = 1.0 / (h*kp + kd); dJointSetSliderParam( slider, dParamLoStop, 0.0 ); // ばねの自然長位置 dJointSetSliderParam( slider, dParamHiStop, 0.0 ); // ばねの自然長位置 dJointSetSliderParam( slider, dParamStopERP, erp ); dJointSetSliderParam( slider, dParamStopCFM, cfm ); ////////////////////////////////////////////////////////// } // 皿 { bowl = new ST_Object; dReal pos[3] = { mx+0.0, my+0.0, mz+1.55 }; dReal size[3] = { 1.0, 1.0, 0.1 }; // body setting bowl- body = dBodyCreate( world ); dBodySetPosition( bowl- body, pos[0], pos[1], pos[2] ); // mass setting dMass mass; dMassSetBoxTotal( mass, 0.001, size[0], size[1], size[2] ); // 1g dBodySetMass( bowl- body, mass ); // geom setting bowl- geom = dCreateBox( space, size[0], size[1], size[2] ); dGeomSetBody( bowl- geom, bowl- body ); // rotation dMatrix3 R; dRFromAxisAndAngle( R, 1.0, 0.0, 0.0, 0.0*M_PI/180 ); dBodySetRotation( bowl- body, R ); bowl- setColor( 1.0, 1.0, 1.0, 1.0 ); // 固定 dJointID fixed = dJointCreateFixed( world, 0 ); dJointAttach( fixed, bowl- body, pole- body ); dJointSetFixed( fixed ); } // ギア0 { gear0 = new ST_Object; dReal pos[3] = { mx+0.0, my+0.0, mz+0.5 }; dReal radius = 0.159; dReal width = 0.2; // body setting gear0- body = dBodyCreate( world ); dBodySetPosition( gear0- body, pos[0], pos[1], pos[2] ); // geom setting gear0- geom = dCreateCylinder( space, gear0_radius, comm_width ); dGeomSetBody( gear0- geom, gear0- body ); dGeomSetCategoryBits( gear0- geom, 0 ); dGeomSetCollideBits( gear0- geom, 0 ); // rotation dMatrix3 R; dRFromAxisAndAngle( R, 1.0, 0.0, 0.0, 90.0*M_PI/180 ); dBodySetRotation( gear0- body, R ); gear0- setColor( 1.0, 0.0, 0.0, 0.5 ); // 軸生成 dJointID hinge = dJointCreateHinge( world, 0 ); dJointAttach( hinge, gear0- body, main_box- body ); dJointSetHingeAnchor( hinge, pos[0], pos[1], pos[2] ); dJointSetHingeAxis( hinge, 0.0, 1.0, 0.0 ); } // ポールとギア0 間の接点位置 保持用 (ダミー) { dReal pos[3] = { mx+gear0_radius, my+0.0, mz+0.5 }; // body setting (ダミー) dBodyID dummy_body = dBodyCreate( world ); dBodySetPosition( dummy_body, pos[0], pos[1], pos[2] ); // mass setting dMass mass; dMassSetSphereTotal( mass, 0.0001, 0.001 ); // 0.1g dBodySetMass( dummy_body, mass ); // ダミーの接点 dummy_contact0 = dJointCreateHinge( world, 0 ); dJointAttach( dummy_contact0, dummy_body, main_box- body ); dJointSetHingeAnchor( dummy_contact0, pos[0], pos[1], pos[2] ); // アンカーは接点の位置に使う dJointSetHingeAxis( dummy_contact0, -1.0, 0.0, 0.0 ); // 軸は接点のnormalで使う } // 目盛針 { needle = new ST_Object; dReal pos[3] = { mx, my-0.65, mz+0.5 }; dReal length = 0.4; // body setting needle- body = dBodyCreate( world ); dBodySetPosition( needle- body, pos[0], pos[1], pos[2] ); // rotation dMatrix3 R; dRFromAxisAndAngle( R, 0.0, 1.0, 0.0, 0.0*M_PI/180 ); dBodySetRotation( needle- body, R ); // geom setting needle- geom = dCreateRay( space, length ); dGeomSetBody( needle- geom, needle- body ); dGeomSetCategoryBits( needle- geom, 0 ); dGeomSetCollideBits( needle- geom, 0 ); needle- setColor( 1.0, 1.0, 1.0, 1.0 ); // 固定 dJointID fixed = dJointCreateFixed( world, 0 ); dJointAttach( fixed, needle- body, gear0- body ); dJointSetFixed( fixed ); } drawObjList.push_back( needle ); drawObjList.push_back( bowl ); drawObjList.push_back( pole ); drawObjList.push_back( gear0 ); drawObjList.push_back( main_box ); drawObjList.push_back( dialplate ); } void updateGearContact() { dJointGroupEmpty( gearjointgroup ); // ポールとギアの接触点を更新 if (dummy_contact0 gear0 pole) { dVector3 c0_pos; dVector3 c0_axis; dJointGetHingeAnchor( dummy_contact0, c0_pos ); dJointGetHingeAxis( dummy_contact0, c0_axis ); //printf( "pos = %f, %f, %f\n", c0_pos[0], c0_pos[1], c0_pos[2] ); //printf( "normal = %f, %f, %f\n", c0_axis[0], c0_axis[1], c0_axis[2] ); dContact contact; contact.geom.depth = 0.0; contact.geom.pos[0] = c0_pos[0]; contact.geom.pos[1] = c0_pos[1]; contact.geom.pos[2] = c0_pos[2]; contact.geom.normal[0] = c0_axis[0]; contact.geom.normal[1] = c0_axis[1]; contact.geom.normal[2] = c0_axis[2]; contact.surface.mode = dContactBounce | dContactSoftERP | dContactSoftCFM; contact.surface.mu = dInfinity; contact.surface.bounce = 0.0; contact.surface.soft_erp = 0.3; contact.surface.soft_cfm = 0.001; dJointID c = dJointCreateContact( world, gearjointgroup, contact ); dJointAttach( c, gear0- body, pole- body ); } } // simulation loop static void simLoop( int pause ) { // Ctl+p が押されたらifに入らない if (!pause) { dSpaceCollide( space, 0, nearCallback ); // 衝突検出 updateGearContact(); dWorldStep( world, step_size ); dJointGroupEmpty( contactgroup ); } // debug if (!pause) { const dReal* pole_pos = dBodyGetPosition( pole- body ); printf( " pole height = %f\n", -1.0+pole_pos[2] ); } // draw object { std vector ST_Object* iterator it = drawObjList.begin(); while (it != drawObjList.end()) { ST_Object *obj = (*it); drawObj( obj ); ++it; } } } int main( int argc, char* argv[] ) { dInitODE(); // setup pointers to drawstuff callback functions dsFunctions fn; fn.version = DS_VERSION; fn.start = start; fn.step = simLoop; fn.command = 0; fn.stop = 0; fn.path_to_textures = "../drawstuff/textures"; world = dWorldCreate(); dWorldSetGravity( world, 0.0, 0.0, -9.8 ); space = dHashSpaceCreate( 0 ); contactgroup = dJointGroupCreate( 0 ); gearjointgroup = dJointGroupCreate( 0 ); dCreatePlane( space, 0, 0, 1, 0 ); createHakari(); // 錘 { ball = new ST_Object; dReal radius = 0.5; dReal pos[3] = { 0.0, 0.0, 3.0 }; // body setting ball- body = dBodyCreate( world ); dBodySetPosition( ball- body, pos[0], pos[1], pos[2] ); // mass setting dMass mass; dMassSetSphereTotal( mass, 5.0, radius); // Kg dBodySetMass( ball- body, mass ); // geom setting ball- geom = dCreateSphere( space, radius ); dGeomSetBody( ball- geom, ball- body ); ball- setColor( 1.0, 1.0, 0.0 ); drawObjList.push_back( ball ); } dsSimulationLoop( argc, argv, 320, 240, fn ); dWorldDestroy( world ); dCloseODE(); return 0; } メモ書き ばね定数の設定 ばね定数は196[N/m]にしたいため、支柱のスライダーの設定を次のようにしました。 このとき、減衰定数は適当に設定しています。 ////////////////////////////////////////////////////////// // バネ・ダンパー設定 dReal h = step_size; dReal kp = 196; // ばね定数 (spring constant) dReal kd = 50.0; // 減衰定数 (damping constant) dReal erp = h*kp / (h*kp + kd ); dReal cfm = 1.0 / (h*kp + kd); dJointSetSliderParam( slider, dParamLoStop, 0.0 ); // ばねの自然長位置 dJointSetSliderParam( slider, dParamHiStop, 0.0 ); // ばねの自然長位置 dJointSetSliderParam( slider, dParamStopERP, erp ); dJointSetSliderParam( slider, dParamStopCFM, cfm ); ////////////////////////////////////////////////////////// ギアと支柱の接触点の位置と垂直ベクトルの求め方 接触点の位置と垂直ベクトル(normal)を求めるのに、ダミーのhingeを作成してそのアンカー位置と軸方向を取得して利用しました。 本来であれば、計算して求めるのが良いのですが、計算が苦手でだったのでこんな方法をとりました。 まとめ 今回、ERPとCFMの設定について、ODEのマニュアル通りにばね・ダンパーの計算を用いることでフックの法則が当てはまることが確認できました。 ギアと上皿を支える支柱との接点を無理矢理にContactJointを作っています。 とりあえず、はかりをそれ程動かさないようならば問題ないのですが、大きく傾けたりしてしまうとはかりがバラバラに分解してしまう不具合があります。 なにか解決方法があれば。。。
https://w.atwiki.jp/bambooflow/pages/287.html
ODEでドミノ倒し ODEでドミノ倒し作成環境 ダウンロード 使用した主なAPI 低スペックなマシンのための設定 まとめ ODEの基本機能を使ってドミノ倒しを作成しました。 遊んでみてください。 コマンド操作 a 牌を置く b 球を発射 x 1つ削除 d 全削除 h カーソル左移動 j カーソル前進 k カーソル後退 l カーソル右移動 m カーソル右回転 n カーソル左回転 + カーソル移動距離+ - カーソル移動距離ー 作成環境 OS Linux(Ubuntu10.10) ODE version 0.11.1 ダウンロード ode_domino01.tgz 使用した主なAPI dWorldCreate dHashSpaceCreate dJointGroupCreate dCreatePlane dWorldSetGravity dBodyCreate dBodySetKinematic - カーソル作成で使用 dBodySetPosition dGeomSetBodydGeomSetBody dGeomSetBodydRFromAxisAndAngle dGeomSetBodydGeomSetRotation dGeomSetBodydGeomSetBody dGeomSetOffsetRotation - カーソル作成で使用 dGeomSetOffsetPosition - カーソル作成で使用 dGeomGetPosition dGeomGetRotation dBodySetAutoDisableFlag - シミュレーション速度確保のため dBodySetAutoDisableLinearThreshold dBodySetAutoDisableAngularThreshold dBodySetAutoDisableSteps dBodyDestroy dGeomDestroy dSpaceCollide dWorldQuickStep dJointGroupEmpty dJointGroupDestroy dAreConnectedExcluding dJointCreateContact dCollide dWorldDestroy dSpaceDestroy dCloseODE 低スペックなマシンのための設定 シミュレーションステップの変更 dWorldQuickStep( world, 0.01f ); // = 変更 AutoDisableの設定 dBodySetAutoDisableFlag(tile- body, 1); dBodySetAutoDisableLinearThreshold(tile- body, 0.3); // = 変更 dBodySetAutoDisableAngularThreshold(tile- body, 0.3); // = 変更 dBodySetAutoDisableSteps(tile- body, 10); 数値は適当です。 この設定ならば私のAtomのNotePCでかろうじて動きます。 まとめ ドミノ倒しのサンプルは前々から作りたいと思っていたのですが、ようやくできました。 こだわりとしてはカーソルの移動のところ。 以前YoutubeにBulletを使ったドミノ倒しをみて対抗?してみました。 プログラムとしては、簡単な機能しか使っていないです。 工夫としては、衝突において、地面の摩擦を無限、牌同士の衝突を0.1と設定しています。 そうしないとうまく動きません。今回のサンプルの中で、少ないポイントの1つです。 あと、当初、dWorldStepを使って実行していたのですが、ドミノが倒れるごとに実行速度が遅くなってしまって、途中でほとんど動かなくなってしました。 ということでサンプルはdWorldQuickStepを使っています。 それと、AutoDisable機能を使って倒れている牌のみを有効にしてシミュレーション速度を確保してみました。 この機能、ちょっとやっかいでStepサイズが大きい(おおざっぱすぎる)とDisableとなる閾値にうまく入ってくれず、うまく機能してくれない場合があります。うまく動かすのに結構手間かもしれません。 Enjoy!!
https://w.atwiki.jp/zukunashi-yarou/pages/35.html
複数のオブジェクトと異なる衝突処理 ODEでは、衝突検出は次のように行います。 dSpaceCollideにより大まかな衝突判定を行い、geom同士が近づいたら引数の関数をコールする(nearCallback関数) 呼ばれたnearCallbackで細かな衝突判定を行い、具体的な形状の衝突を判定(dJointCreateContact) もし、衝突を検出したらContactJointにより衝突計算を行う 衝突時、オブジェクトごとに反発係数や摩擦係数は異なるのが自然ですが、 ODEの衝突処理は、nearCalllback関数の1カ所で行うことになるため、 異なる衝突処理を表現するのになかなかに困惑します。 このページでは、オブジェクトごとに衝突処理を切り替えるコード例を示したいと思います。 あくまでも一例です。ほかに良い方法は見つかるかもしれませんが参考にしていただければ幸いです。 3つの球体と異なるバウンド処理 ここで紹介する方法は、dGeomSetDataとdGeomGetDataを使って異なる設定を行うというものです。 オブジェクト生成時にdSurfaceParametersを含むクラスのポインタをdGeomIDに設定(dGeomSetData)し、 nearCallback関数が呼ばれたら、dGeomIDのクラスポインタを取得(dGeomGetData)し、 dSurfaceParameters値を設定します。 そうすると、オブジェクトごとに設定された異なるサーフェイスパラメータが反映されます。 // v0.13 #include vector #include iostream #include cassert #ifdef dDOUBLE #define dsDrawSphere dsDrawSphereD #define dsDrawBox dsDrawBoxD #define dsDrawCylinder dsDrawCylinderD #define dsDrawCapsule dsDrawCapsuleD #define dsDrawLine dsDrawLineD #endif class Shape { public dSurfaceParameters m_surface; virtual void draw() {} Shape() { m_surface.mode = 0; m_surface.mu = dInfinity; } }; class Sphere public Shape { protected dBodyID m_body; dGeomID m_geom; int m_kind; public Sphere(dWorldID world, dSpaceID space, int kind=0) m_kind(kind) { dReal radius = 0.5; // 生成 m_body = dBodyCreate( world ); m_geom = dCreateSphere(space, radius); dGeomSetBody(m_geom, m_body); // データ設定 dGeomSetData(m_geom, this); // 位置設定 if (m_kind == 0) { dBodySetPosition(m_body, -2.0, 0.0, 3.0); } else if (m_kind == 1) { dBodySetPosition(m_body, 0.0, 0.0, 3.0); } else if (m_kind == 2) { dBodySetPosition(m_body, 2.0, 0.0, 3.0); } // 質量設定 dMass m; dMassSetZero( m); dMassSetSphereTotal( m, 1.0, radius); dBodySetMass(m_body, m); // 衝突時のサーフェスパラメータ設定 if (m_kind == 0) { //m_surface.mode = dContactBounce | dContactSoftCFM; m_surface.mode = dContactBounce; m_surface.mu = dInfinity; // 摩擦係数 m_surface.bounce = 1.00; // 反発係数 m_surface.bounce_vel = 0.1; // 反発係数しきい値 //m_surface.soft_cfm = 0.005; // CFM設定 } else if (m_kind == 1) { m_surface.mode = dContactBounce | dContactSoftCFM; m_surface.mu = dInfinity; // 摩擦係数 m_surface.bounce = 1.00; // 反発係数 m_surface.bounce_vel = 0.1; // 反発係数しきい値 m_surface.soft_cfm = 0.005; // CFM設定 } else if (m_kind == 2) { m_surface.mode = dContactBounce | dContactSoftCFM; m_surface.mu = dInfinity; // 摩擦係数 m_surface.bounce = 0.80; // 反発係数 m_surface.bounce_vel = 0.1; // 反発係数しきい値 m_surface.soft_cfm = 0.001; // CFM設定 } } ~Sphere() { dBodyDestroy(m_body); dGeomDestroy(m_geom); } virtual void draw() { //dsSetTexture (DS_CHECKERED); dsSetTexture (DS_WOOD); //dsSetColor( 1.0f, 1.0f, 0.0f ); // RGB Color if (m_kind == 0) { dsSetColorAlpha(0.75, 0.3, 0.3, 1.0); //赤 } else if (m_kind == 1) { dsSetColorAlpha(0.3, 0.75, 0.3, 1.0); //緑 } else if (m_kind == 2) { dsSetColorAlpha(0.3, 0.3, 0.75, 1.0); //青 } dsDrawSphere( dGeomGetPosition( m_geom ), dGeomGetRotation( m_geom ), dGeomSphereGetRadius(m_geom) ); } }; static std vector Shape* shapeList; static dWorldID world; static dSpaceID space; static dJointGroupID contact_group; dSurfaceParameters default_surface = { .mode = 0, .mu = dInfinity }; // 衝突検出用関数 static void nearCallback( void *data, dGeomID o1, dGeomID o2 ) { static Shape defaultShape; static const int MAX_CONTACTS = 4; dBodyID b1 = dGeomGetBody( o1 ); dBodyID b2 = dGeomGetBody( o2 ); if ( b1 b2 dAreConnectedExcluding( b1, b2, dJointTypeContact ) ) return; // オブジェクトのサーフェイスパラメータを取得 Shape *s; s = (Shape*)dGeomGetData(o1); if (!s) { s = (Shape*)dGeomGetData(o2); } if (!s) { s = defaultShape; } assert(s); dContact contact[MAX_CONTACTS]; int numc = dCollide( o1, o2, MAX_CONTACTS, contact[0].geom, sizeof( dContact ) ); if (numc 0) { for (int i=0; i numc; i++) { // 物体同士の接触時のパラメータ設定 contact[i].surface = s- m_surface; // 衝突の発生 dJointID c = dJointCreateContact( world, contact_group, contact+i ); dJointAttach( c, b1, b2 ); } } } // start simulation - set viewpoint static void start() { static float xyz[3] = { 0.f, 5.f, 3.f }; static float hpr[3] = {-90.f, -15.f, 0.f }; dsSetViewpoint( xyz, hpr ); // カメラ位置と方向設定 } // simulation loop static void simLoop( int pause ) { // Ctl+p が押されたらifに入らない if (!pause) { dSpaceCollide( space, 0, nearCallback ); // 衝突検出 dWorldStep( world, 0.01 ); dJointGroupEmpty( contact_group ); } // Shapeの表示 std vector Shape* iterator it = shapeList.begin(); while (it != shapeList.end()) { (*it)- draw(); ++it; } } int main( int argc, char* argv[] ) { // setup pointers to drawstuff callback functions dsFunctions fn; fn.version = DS_VERSION; fn.start = start; fn.step = simLoop; fn.command = 0; fn.stop = 0; fn.path_to_textures = "../../../textures"; // ODE初期化 dInitODE2(0); dAllocateODEDataForThread(dAllocateMaskAll); // World,Space生成 world = dWorldCreate(); dWorldSetGravity(world, 0.0, 0.0, -9.8); //dWorldSetCFM (world,1e-5); //dWorldSetLinearDamping(world, 0.00001); //dWorldSetAngularDamping(world, 0.005); //dWorldSetMaxAngularSpeed(world, 200); //dWorldSetERP(world, 0.2); //dWorldSetContactSurfaceLayer (world,0.001); space = dSimpleSpaceCreate (0); contact_group = dJointGroupCreate(0); // 地面生成 dGeomID ground = dCreatePlane(space, 0, 0, 1, 0); dGeomSetData(ground, NULL); // シェイプ生成 shapeList.push_back(new Sphere(world, space, 0)); shapeList.push_back(new Sphere(world, space, 1)); shapeList.push_back(new Sphere(world, space, 2)); dsSimulationLoop(argc, argv, 320, 240, fn); dJointGroupDestroy(contact_group); dWorldDestroy(world); dSpaceDestroy(space); dCloseODE(); return 0; }
https://w.atwiki.jp/sandbox2-wiki/pages/20.html
オブジェクトGeomEntity 配置用のオブジェクト。武器、兵器等は配置されてるだけで実際には使用できない。 また攻撃しても壊れない。
https://w.atwiki.jp/nopu/pages/107.html
一変数だけの微分ならODE つまり,時間の関数についての方程式はODE 回路方程式,運動方程式,etc... 微分方程式をどうやって調べるか? ほとんどの方程式は解析的に解けない(19c末の認識) → 位相力学的方法(Poincare) 1. 解の存在 2. 解の一意性 3. 解の連続性 ←適当なパラメータ(初期値とか)依存性のこと。 基本となる例 Prop. 平均値の定理の系 I=[a,b] f∈D(I) (←平均値の定理が使えるための条件) 特に,f (x)=0 のとき fは定数 Cor. f =const. f∈D(I) Prop. f =f Th. Picard-Lindelöf (詳細は「一般のODE」にある) 初期値問題 f(x) I→f(I)⊂J⊂R; unknown conti. φ(x,y) I×J→R; bilinear φがで有界かつ連続とする。 さらに,このyに対してリプシッツ連続とする。 このとき,x0の適当な近傍Bh(x0)で唯一の解が存在する。 線形の場合 Cauchy問題(1階線形ODEの初期値問題)(1) Th. 線形常微分方程式の解の一意存在 pi(t)とq(t)が開区間Iで連続ならば,Iでユニークな解x(t)が存在する。 Cor. 同次方程式の基本解 q(t)=0とした方程式(同次方程式)(2) の一般解は,n個の独立な基本解の線形結合で与えられる。 Th. 基本解の独立性判定法 n個の基本解が互いに独立 ⇔ 基本解の Wronskian が0にならない。 Def. 正規形 一般のODE 解の存在する区間は,線形のときほど広くない。 初期値問題(1) ただし、 Prop. (1) は以下の積分と同値 とりあえず(1)を積分してみれば分かる。 Th. Picardの逐次近似法 初期値問題(1)の局所解の構成法 f C1級 とする。 関数列 {vk} を以下で定義すると, これは区間 上で一様収束し、その極限は(1)のIδにおける局所解である。 Th. Cauchyの折れ線近似 f 連続の場合に、初期値問題(1)の局所解の存在を示す方法(その1) Eulerの差分法 以下の数列{u0}を考える。 点列を順に結んで得られる折れ線グラフをuεとする(Cauchyの折れ線関数)。 関数族 {} の一様有界かつ同等連続になることが示されるので、Ascoli-Alzelaの定理によって適当なεの減少列{εj}が取れて、関数列{}は適当な区間Iδ上で一様収束する。 得られる極限関数が局所解である。 Cor. 大域解の存在 f Lipschitz 連続のときは、大域に拡張できる。 Th. Schauderの不動点定理 f 連続の場合に局所解の存在を示す方法(その2) 次の積分作用素が不動点を持つことを示せばよい。 Fは適当なIδ上のコンパクト作用素になるので、Schauderの不動点定理により、少なくとも1つ不動点を持つ。 解の一意性 fがC1級のとき、初期値問題(1)の解はあれば一意。 証明には Gronwallの補題 を使う。 Cor. 解の初期値に対する連続依存性 さらに、初期値ηを解u(t;η)のパラメータとみたてると、uはt,ηの連続関数である。 Lem. Gronwallの補題
https://w.atwiki.jp/bambooflow/pages/98.html
重心の移動 物体の重心をずらす方法について説明します。 サンプルコードでは重心をずらした球体を地面に落としてみました。 重心がずれていることで球体は垂直にバウンドせずに斜めに跳ね返りました。 黄色の球体の中に見える青色の点がずらした重心位置です。 説明 重心を移動するにはソースコードにdGeomSetOffsetPosition関数を追加するだけです。 この関数は、bodyに対してgeomをどれだけずらすかを設定できるものです。 これは重心を移動するのではなく、重心位置(body)から衝突物体(geom)位置をずらすイメージになります。 コードでpos[]が物体の位置でg_pos[]が重心位置をずらす値です。 dBodySetPosition関数によってpos+g_posに配置した後、dGeomSetOffsetPosition関数によって-g_posして物体位置を補正しています。 dGeomSetOffsetPosition関数を使用する際の注意として、geomにbodyが設定された状態でこの関数を呼ぶ必要があります。 もし設定されていない状態で使用すると思わぬエラーが出力されます。 // create Sphere dReal pos[3] = { 0.0, 0.0, 5.0 }; // 物体位置(絶対値) dReal g_pos[3] = { 0.1, 0.0, 0.0 }; // 重心位置(相対値) b_sphere = dBodyCreate( world ); // set dBodyID dBodySetPosition( b_sphere, pos[0]+g_pos[0], pos[1]+g_pos[1], pos[2]+g_pos[2] ); dMass sphere_mass; sphere_mass.setSphere( 1.0, radius ); dBodySetMass( b_sphere, sphere_mass ); // 質量設定 g_sphere = dCreateSphere( space, radius ); // set dGeomID //dGeomSetPosition( g_sphere, pos[0], pos[1], pos[2] ); dGeomSetBody( g_sphere, b_sphere ); dGeomSetOffsetPosition( g_sphere, -g_pos[0], -g_pos[1], -g_pos[2] ); // 重心位置変更 関連 void dGeomSetOffsetPosition (dGeomID, dReal x, dReal y, dReal z); void dGeomSetOffsetRotation (dGeomID, const dMatrix3 R); void dGeomSetOffsetQuaternion (dGeomID, const dQuaternion Q); http //opende.sourceforge.net/mediawiki-1.6.10/index.php/Manual_%28Collision_Detection%29#Geometry_Transform_Class サンプルコード move_gravity_point.tgz 以上
https://w.atwiki.jp/zukunashi-yarou/pages/38.html
コンポジット・オブジェクトを使った空箱の作成 空の箱サンプルプログラムを用意してみました。 ここではコンポジット・オブジェクト(GeomTransform)を利用しています。 Massの設定はうまくいかなかったので一部コメントアウトしました。 #include ode/ode.h #include drawstuff/drawstuff.h #include list #ifdef dDOUBLE #define dsDrawSphere dsDrawSphereD #define dsDrawBox dsDrawBoxD #define dsDrawCylinder dsDrawCylinderD #define dsDrawCapsule dsDrawCapsuleD #define dsDrawLine dsDrawLineD #endif class Shape { public virtual void draw() = 0; }; class Sphere public Shape { protected dBodyID m_body; dGeomID m_geom; public Sphere(dWorldID world, dSpaceID space, dReal x, dReal y, dReal z) { dReal radius = 0.2; // 生成 m_body = dBodyCreate( world ); m_geom = dCreateSphere(space, radius); dGeomSetBody(m_geom, m_body); // 位置設定 //dBodySetPosition(m_body, 0.0, 0.0, 3.0); dBodySetPosition(m_body, x, y, z); // 質量設定 dMass m; dMassSetZero( m); dMassSetSphereTotal( m, 1.0, radius); dBodySetMass(m_body, m); } ~Sphere() { dBodyDestroy(m_body); dGeomDestroy(m_geom); } virtual void draw() { //dsSetTexture (DS_CHECKERED); dsSetTexture (DS_WOOD); //dsSetColor( 1.0f, 1.0f, 0.0f ); // RGB Color dsSetColorAlpha(0.75, 0.3, 0.3, 1.0); dsDrawSphere( dGeomGetPosition( m_geom ), dGeomGetRotation( m_geom ), dGeomSphereGetRadius(m_geom) ); } }; class Hako public Shape { protected static const int NUM = 5; dBodyID m_body; dGeomID m_geom[NUM]; public Hako(dWorldID world, dSpaceID space) { const dReal scale = 2.0; const dVector3 lengths = {1.0*scale, 1.0*scale, 0.01}; // offset{x,y,z}, angle{ax, ay, az} const dReal offsets[NUM][3] = { {-0.5*scale, 0.0*scale, 0.0*scale}, { 0.5*scale, 0.0*scale, 0.0*scale}, { 0.0*scale,-0.5*scale, 0.0*scale}, { 0.0*scale, 0.5*scale, 0.0*scale}, { 0.0*scale, 0.0*scale,-0.5*scale} }; const dReal angles[NUM][3] = { { 0.0,90.0, 0.0}, { 0.0,90.0, 0.0}, {90.0, 0.0, 0.0}, {90.0, 0.0, 0.0}, { 0.0, 0.0, 0.0} }; // 生成 m_body = dBodyCreate( world ); // Transform 生成 dMass m, tran_m; dMassSetZero( m); dGeomID t_geom[NUM]; for (int k=0; k NUM; k++) { m_geom[k] = dCreateGeomTransform(space); dGeomTransformSetCleanup(m_geom[k],1); t_geom[k] = dCreateBox(0, lengths[0], lengths[1], lengths[2]); //dMassSetBox( tran_m, 1.0, lengths[0], lengths[1], lengths[2]); dGeomTransformSetGeom(m_geom[k], t_geom[k]); dGeomSetPosition(t_geom[k], offsets[k][0], offsets[k][1], offsets[k][2]); //dMassTranslate( tran_m, offsets[k][0], offsets[k][1], offsets[k][2]); dMatrix3 rotation; dMatrix3 r1, r2, r3, r4; dRFromAxisAndAngle(r1, 1.0, 0.0, 0.0, angles[k][0]*M_PI/180.0); // x-axis dRFromAxisAndAngle(r2, 0.0, 1.0, 0.0, angles[k][1]*M_PI/180.0); // y-axis dRFromAxisAndAngle(r3, 0.0, 0.0, 1.0, angles[k][2]*M_PI/180.0); // z-axis dMultiply0_333(r4, r1, r2); dMultiply0_333(rotation, r3, r4); dGeomSetRotation(t_geom[k], rotation); //dMassRotate( tran_m, rotation); //dMassAdd( m, tran_m); } // body, geom, mass関連づけ for (int k=0; k NUM; k++) { dGeomSetBody(m_geom[k], m_body); } dMassSetBoxTotal( m, 10.0, lengths[0], lengths[1], lengths[2]); dBodySetMass(m_body, m); dBodySetPosition(m_body, 0.0, 0.0, 0.51*scale); } ~Hako() { dBodyDestroy(m_body); for (int k=0; k NUM;k++) { dGeomDestroy(m_geom[k]); } } virtual void draw() { for (int k=0; k NUM; k++) { dGeomID t_geom = dGeomTransformGetGeom( m_geom[k] ); dVector3 lengths; dGeomBoxGetLengths(t_geom, lengths); // Get world position/orientation of parent body const dReal* pBodyPos = dBodyGetPosition( m_body ); const dReal* pBodyRotMat = dBodyGetRotation( m_body ); // Get local position/orientation of component within geom transform const dReal* pLocalPos = dGeomGetPosition (t_geom); const dReal* pLocalRotMat = dGeomGetRotation (t_geom); dVector3 worldPos; dMatrix3 worldRotMat; dMULTIPLY0_331( worldPos, pBodyRotMat, pLocalPos ); worldPos[0] += pBodyPos[0]; worldPos[1] += pBodyPos[1]; worldPos[2] += pBodyPos[2]; dMULTIPLY0_333( worldRotMat, pBodyRotMat, pLocalRotMat ); //dQuaternion worldQuat; //dQfromR(worldQuat, worldRotMat); dsSetColorAlpha(0.75, 0.75, 1.0, 0.5); dsDrawBox( worldPos, worldRotMat, lengths ); } } }; static std list Shape* shapeList; static dWorldID world; static dSpaceID space; static dJointGroupID contact_group; // 衝突検出用関数 static void nearCallback( void *data, dGeomID o1, dGeomID o2 ) { static const int MAX_CONTACTS = 4; dBodyID b1 = dGeomGetBody( o1 ); dBodyID b2 = dGeomGetBody( o2 ); if ( b1 b2 dAreConnectedExcluding( b1, b2, dJointTypeContact ) ) return; dContact contact[MAX_CONTACTS]; int numc = dCollide( o1, o2, MAX_CONTACTS, contact[0].geom, sizeof( dContact ) ); if (numc 0) { for (int i=0; i numc; i++) { // 物体同士の接触時のパラメータ設定 contact[i].surface.mode = dContactBounce | dContactSoftCFM; contact[i].surface.mu = dInfinity; // 摩擦係数 contact[i].surface.bounce = 0.7; // 反発係数 contact[i].surface.bounce_vel = 0.1; contact[i].surface.soft_cfm = 0.001; // CFM設定 // 衝突の発生 dJointID c = dJointCreateContact( world, contact_group, contact+i ); dJointAttach( c, b1, b2 ); } } } // start simulation - set viewpoint static void start() { static float xyz[3] = { 0.f, 5.f, 3.f }; static float hpr[3] = {-90.f, -15.f, 0.f }; dsSetViewpoint( xyz, hpr ); // カメラ位置と方向設定 } // simulation loop static void simLoop( int pause ) { // Ctl+p が押されたらifに入らない if (!pause) { dSpaceCollide( space, 0, nearCallback ); // 衝突検出 dWorldQuickStep( world, 0.01 ); dJointGroupEmpty( contact_group ); } // Shapeの表示 std list Shape* iterator it = shapeList.begin(); while (it != shapeList.end()) { (*it)- draw(); ++it; } } static void command(int key) { dReal x = dReal(rand()%10) / 10000.0; dReal y = dReal(rand()%10) / 10000.0; dReal z = dReal(rand()%10) / 10000.0 + 5.0; shapeList.push_front(new Sphere(world, space, x, y, z)); } int main( int argc, char* argv[] ) { // setup pointers to drawstuff callback functions dsFunctions fn; fn.version = DS_VERSION; fn.start = start; fn.step = simLoop; fn.command = command; fn.stop = 0; fn.path_to_textures = "../../../textures"; // ODE初期化 dInitODE2(0); dAllocateODEDataForThread(dAllocateMaskAll); // World,Space生成 world = dWorldCreate(); dWorldSetGravity(world, 0.0, 0.0, -9.8); dWorldSetCFM (world,1e-5); dWorldSetLinearDamping(world, 0.00001); dWorldSetAngularDamping(world, 0.005); dWorldSetMaxAngularSpeed(world, 200); dWorldSetERP(world, 0.2); dWorldSetContactSurfaceLayer (world,0.001); space = dHashSpaceCreate (0); contact_group = dJointGroupCreate(0); // 地面生成 dCreatePlane(space, 0, 0, 1, 0); // シェイプ生成 shapeList.push_back(new Hako(world, space)); dsSimulationLoop(argc, argv, 320, 240, fn); dJointGroupDestroy(contact_group); dWorldDestroy(world); dSpaceDestroy(space); return 0; }
https://w.atwiki.jp/bambooflow/pages/285.html
ODE:cmakeでコンパイル ODE:cmakeでコンパイルLinux(Ubuntu10.10)でcmakeインストール CMakeList.txtの準備 サンプル実行 その他のCMakeオプション cmakeはMakefileより便利です。 Makefileをごりごり作るよりもcamkeをインストールして、ちょっと使い方を覚えた方が手間が省けます。 ということで、ODEでのcmakeの導入についてメモします。 Linux(Ubuntu10.10)でcmake インストール 次のコマンドを実行。 % sudo apt-get install cmake 最近のLinuxのディストリビューションならばapt-getとかyumとかでインストールできると思われます。 CMakeList.txtの準備 odeは、makeまで実行した状態でOK。(make installはしなくてよい) set(PROJ_NAME hello) set(TARGET ${PROJ_NAME}) # プロジェクト名 project(${PROJ_NAME}) set(ODE_PATH ../ode-0.11.1) set(ODE_INC_PATH ${ODE_PATH}/include) set(ODE_LIB_PATH ${ODE_PATH}/ode/src/.libs) set(DS_INC_PATH ${ODE_PATH}/include) set(DS_LIB_PATH ${ODE_PATH}/drawstuff/src/.libs) # インクルードパスの追加 include_directories(${ODE_INC_PATH} ${DS_INC_PATH}) # ライブラリパスの追加 link_directories(${ODE_LIB_PATH} ${DS_LIB_PATH}) # マクロ設定 [-D] add_definitions(-DdDOUBLE) # 実行ファイルとソースファイル群 add_executable(${TARGET} ${PROJ_NAME}.cpp) # ライブラリ target_link_libraries(${TARGET} m ode drawstuff GLU GL) サンプル実行 とりあえず適当にファイルを準備。 hello.cpp #include ode/ode.h #include stdio.h int main(int argc, char* argv[]) { printf("Cmake de Hello ODE!!!\n"); return 0; } hello.cppとCMakeList.txtが同じディレクトリにある状態で次のコマンドを実行。 % cmake . すると、Makefileが生成される。 あとは、コンパイル、そして実行。 % make % ./hello Cmake de Hello ODE!!! 無事に実行できれば、OKです。 その他のCMakeオプション メモ。 # 要求するCMakeのバージョン cmake_minimum_required(VERSION 2.6) # build type set(CMAKE_BUILD_TYPE Debug) # compile option (DEBUG) set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall") set(CMAKE_C_FLAGS_DEBUG "-g -Wall") # compile option (Release) set(CMAKE_CXX_FLAGS_RELEASE "-Wall -O3") set(CMAKE_C_FLAGS_RELEASE "-Wall -O3") 以上
https://w.atwiki.jp/zukunashi-yarou/pages/33.html
ode-0.13メモ 新機能 implicit gyroscopic forces が安定した transmission joint が追加になった rolling friction が追加になった Make時に"-DdDOUBLE"が不要になった. かわりに ./configure時に"include/ode/precision.h"が生成される. Double Ball joint が追加になった(別名 "distance joint") Double Hinge joint が追加になった スレッド実行用のインタフェースが追加